package com.segersten.othello.service.akka.lobby;

import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.beust.jcommander.internal.Lists;
import com.google.common.collect.Maps;
import com.segersten.othello.service.akka.Actor;
import com.segersten.othello.service.akka.Auth;
import com.segersten.othello.service.akka.GameObjectPool;
import com.segersten.othello.service.akka.GameObjectReference;
import com.segersten.othello.service.akka.Player;
import com.segersten.othello.service.akka.game.BoardActorFactory;
import com.segersten.othello.service.akka.game.DefaultBoardActorFactory;
import com.segersten.othello.service.akka.game.OthelloBoardServiceActor;
import com.segersten.othello.service.akka.game.SessionLogginAction;
import com.segersten.othello.service.akka.game.dao.GameDao;
import com.segersten.othello.service.akka.lobby.dao.LoginRegistryDao;
import com.segersten.othello.service.akka.lobby.dao.OngoingGamesDao;
import com.segersten.othello.service.akka.lobby.dao.PlayerScoreDao;
import com.segersten.othello.service.akka.lobby.dao.RegisteredForMatchingDao;
import com.segersten.othello.service.rest.CreateBoardRequest;

public class LobbyServiceActor implements Actor {

	public static final String SERVICE_NAME = "lobby";
	private PlayerScoreDao playerScoresDao;
	private BoardActorFactory boardActorFactory;
	private LoginRegistryDao loginRegistryDao;
	private OngoingGamesDao ongoingGamesDao;
	private GameObjectPool gameObjectPool;
	private RegisteredForMatchingDao registeredForMatchingDao; 
	private static Logger logger = Logger.getLogger(LobbyServiceActor.class.getSimpleName());
	
	public LobbyServiceActor(GameObjectPool gameObjectPool, 
	                        BoardActorFactory factory, 
	                        RegisteredForMatchingDao registeredForMatchingDao, 
	                        PlayerScoreDao playerScoreDao,
	                        LoginRegistryDao loginRegistryDao,
	                        OngoingGamesDao ongoingGamesDao) {
	    this.loginRegistryDao = loginRegistryDao;
		this.playerScoresDao = playerScoreDao;
		this.boardActorFactory = factory;
		this.gameObjectPool = gameObjectPool;
		this.registeredForMatchingDao = registeredForMatchingDao;
		this.ongoingGamesDao = ongoingGamesDao;
	}
	
	@Override
    public Object onReceive(GameObjectPool objectPool, GameDao game, Object message) throws Exception {
	    if (logger.isLoggable(Level.FINE)) {
	        logger.fine("LobbyServiceActor.Received " + message);
	    }
		if (message instanceof LoginAction) {
		    LoginAction action = (LoginAction) message;
		    return handleLoginAction(action);
        } else if (message instanceof MatchOpponentsAction) {
            return handleMatchOpponents();
		} else if (message instanceof RegisterAction) {
			RegisterAction action = (RegisterAction) message;
			return handleRegister(action);
		} else if (message instanceof GameFinishedAction) {
			GameFinishedAction action = (GameFinishedAction) message;
			return handleGameFinished(action);
		} else if (message instanceof StateAction) {
			StateAction action = (StateAction) message;
			return tellState(action);
		} else if (message instanceof PlayerScoreAction) {
			PlayerScoreAction action = (PlayerScoreAction) message;
			return tellScore(action);
		} else if (message instanceof CancelTicketAction) {
			CancelTicketAction action = (CancelTicketAction) message;
			return cancelTickets(action);
		} else if (message instanceof CreateBoardRequest) {
		    CreateBoardRequest action = (CreateBoardRequest) message;	
		    return handleCreateBoardRequest(action);
		} else if (message instanceof String) {
	            String action = (String) message;
                return handleString(action);
		} else {
			throw new IllegalArgumentException("Unknown message: " + message);
		}
	}

	private String handleCreateBoardRequest(CreateBoardRequest action)
    {
	    BoardReference boardReference = createBoard(action.getPlayer1(), action.getPlayer2());
        return boardReference.getReference();
    }

    private Object handleString(String action)
    {
       if ("sessions".equals(action)) {
           return loginRegistryDao.getAllSessions();
       } else {
           return null;
       }
    }

    private Object handleLoginAction(LoginAction action)
    {
	    Player player = action.getPlayer();
        String nick = player.getNickname();
	    String domain = player.getAuth().getDomain();
        Auth replacedSession = loginRegistryDao.addSession(domain, nick, player.getAuth());
        if (logger.isLoggable(Level.FINE)) {
            logger.fine("Replaced login " + replacedSession);
        }
        if (replacedSession != null) {
            List<Game> games = ongoingGamesDao.getOngoingGames(player.domainPlayer());
            for (Game game : games) {
                GameObjectReference ref = gameObjectPool.actorFor(game.getBoardReference().getReference());
                gameObjectPool.tell(ref, new SessionLogginAction(replacedSession, player));
            }
        } else {
            // do nothing
        }
	    return null;
    }

    private Object cancelTickets(CancelTicketAction action) {
        if (!loginRegistryDao.isAuthorized(action.getAuth())) {
            return LobbyResponseStatus.NonAuth;
        }
		DomainPlayer dp = loginRegistryDao.getDomainPlayer(action.getAuth());
		registeredForMatchingDao.remove(dp);
		return LobbyResponseStatus.OK;
	}

	private Object tellScore(PlayerScoreAction action) {
		DomainPlayer player = new DomainPlayer(action.getDomain(), action.getPlayerId());
        return playerScoresDao.getPoints(player);
	}

	private Object handleGameFinished(GameFinishedAction action) {
		Map<DomainPlayer, Integer> scores = Maps.newHashMap();
		for(Entry<Auth, Integer> entry : action.getScores().entrySet()) {
		    scores.put(loginRegistryDao.getDomainPlayer(entry.getKey()), entry.getValue());
		}
		this.ongoingGamesDao.removeGame(action.getBoardReference());
        playerScoresDao.updateScores(action.getBoardReference(), scores);
		return null;
	}

	private Object handleMatchOpponents() {
	    
	    List<DomainPlayer> pair = registeredForMatchingDao.extractPair();
		while (pair != null) {
			//System.out.println("Match " + registeredForMatching);
			Iterator<DomainPlayer> iter = pair.iterator();
			

			// Create an actorReference
			DomainPlayer domainPlayer1 = iter.next();
			DomainPlayer domainPlayer2 = iter.next();
			Player player1 = loginRegistryDao.getPlayer(domainPlayer1);
			Player player2 = loginRegistryDao.getPlayer(domainPlayer2);
            createBoard(player1, player2);
			
			pair = registeredForMatchingDao.extractPair();
		}
		return LobbyResponseStatus.OK;
	}

    private BoardReference createBoard(Player player1, Player player2)
    {
        String actorRef = boardActorFactory.createBoard(player1, player2);
        //String actorRef = AkkaUtil.getReference(actor);

        BoardReference boardReference = new BoardReference(actorRef);
        ongoingGamesDao.addGame(boardReference, player1.domainPlayer(), player2.domainPlayer());
        
        return boardReference;
    }

	private Object handleRegister(RegisterAction action) {
	    Player player = action.getPlayer();
	    
	    if (false == loginRegistryDao.isAuthorized(player.getAuth())) {
            return LobbyResponseStatus.NonAuth;
        }
	    
        DomainPlayer domainPlayer = new DomainPlayer(player.getAuth().getDomain(), player.getNickname());
        registeredForMatchingDao.put(domainPlayer, new Ticket(domainPlayer));
		return LobbyResponseStatus.OK;
	}
	
	private Object tellState(StateAction action) {
	    Auth auth = action.getAuth();
		if (this.loginRegistryDao.isAuthorized(auth)==false) {
		    return new StateResponse(LobbyResponseStatus.NonAuth);
		}
		DomainPlayer dp = loginRegistryDao.getDomainPlayer(auth);
		LobbyState stateResponse = new LobbyState(this.registeredForMatchingDao.get(dp), 
		            this.ongoingGamesDao.getOngoingGames(dp));
		return new StateResponse(stateResponse);
	}

}
